const iconv = require('iconv-lite');
const crypto = require('crypto');
const fs = require('fs');

function getFileHash(filePath) {
    return new Promise((resolve, reject) => {
        const hash = crypto.createHash('sha256');
        const stream = fs.createReadStream(filePath);

        stream.on('error', (err) => {
            reject(err);
        });

        stream.on('data', (chunk) => {
            hash.update(chunk);
        });

        stream.on('end', () => {
            const digest = hash.digest('hex');
            resolve(digest);
        });
    });
}

function textCoding(text) {
    return iconv.decode(Buffer.from(text, 'latin1'), 'utf-8');
}

async function runTasksInParallel(tasks, maxConcurrent) {
    const queue = [...tasks];
    const running = [];

    const runNext = async () => {
        while (queue.length > 0 && running.length < maxConcurrent) {
            const task = queue.shift();
            running.push(task());
        }
        await Promise.race(running);
        for (let i = 0; i < running.length; i++) {
            let status = await promiseState(running[i]);
            status = status.status;
            if (status === 'rejected') {
                throw promiseState(running[i]).reason;
            }
            if (status === 'pending') {
                continue;
            }
            if (status === 'fulfilled') {
                running.splice(i, 1); // Remove finished task
            }
        }
        if (!running.length) {
            return;
        }
        await runNext();
    };

    await runNext();
}

function promiseState(promise) {
    const pendingState = { status: 'pending' };

    return Promise.race([promise, pendingState]).then(
        (value) =>
            value === pendingState ? value : { status: 'fulfilled', value },
        (reason) => ({ status: 'rejected', reason })
    );
}

module.exports = {
    getFileHash,
    textCoding,
    runTasksInParallel,
};
